package com.ctl.utils;


import com.ctl.common.model.BindGoods;
import com.ctl.common.model.Goods;
import com.ctl.test.po.Person;
import com.ctl.test.po.Person1;
import com.ctl.test.po.Student;
import com.ctl.test.po.Teacher;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.flipkart.zjsonpatch.DiffFlags;
import com.flipkart.zjsonpatch.JsonDiff;
import lombok.extern.slf4j.Slf4j;
import com.fasterxml.jackson.core.type.*;
import org.apache.commons.lang3.reflect.TypeUtils;
import com.ctl.utils.jsondeserializer.*;
import com.ctl.utils.jsonserializer.*;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.*;

import static com.flipkart.zjsonpatch.DiffFlags.*;

/**
 * <p>Title: JacksonMapper</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2019</p>
 * <p>Company: www.hanshow.com</p>
 *
 * @author ctl
 * @version 1.0
 * @date 2020-04-15 22:11
 */
@Slf4j
public class JacksonMapper extends ObjectMapper {
    private static final String language = "zh-CN"; // 中文

    public JacksonMapper() {
        super();
        // 示例语言环境
        String[] split = language.replaceAll("-", "_").split("_");
        Locale locale = new Locale(split[0]);
        if (split.length == 2) {
            locale = new Locale(split[0], split[1]);
        }
        this.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        this.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
        this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        module.addSerializer(long.class, ToStringSerializer.instance);
        // 配置将 char[] 序列化为字符串数组
        module.addSerializer(char[].class, new CharArraySerializer());//char[] 默认将数组序列化为一个字符串
        // 配置将 BigDecimal 和 BigInteger 序列化为字符串
        module.addSerializer(BigDecimal.class, new BigDecimalToStringSerializer());
        module.addSerializer(BigInteger.class, new BigIntegerToStringSerializer());
        //非固定格式使用使用对应地区的格式化方式
        //配置 Date 的序列化器和反序列化器，基于语言环境动态设置格式
        SimpleDateFormat dateFormat = new SimpleDateFormat(getDateFormatForLocale(locale), locale);
        module.addSerializer(Date.class, new DateSerializer(false, dateFormat));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale)));
        module.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).withLocale(locale)));
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(locale)));
        module.addSerializer(ObjectId.class, new ObjectIdSerializer());
        module.addDeserializer(ObjectId.class, new ObjectIdDeserializer());

        registerModule(module);

    }
    /**
     * 根据语言获取java.util.Date对应的格式
     *
     * @param locale
     * @return
     */
    public static String getDateFormatForLocale(Locale locale) {
        //只有日期
//        DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
        //日期+时间
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, locale);
        if (dateFormat instanceof SimpleDateFormat) {
            return ((SimpleDateFormat) dateFormat).toPattern();
        }
        throw new IllegalArgumentException("Unable to determine date format for locale: " + locale);
    }
    private static JacksonMapper mapper = new JacksonMapper();

    /**
     * obj to json 线程安全
     *
     * @param value
     * @return
     */
    public static String objToJsonString(Object value) {
        String json = null;
        try {
            json = mapper.writeValueAsString(value);
        } catch (Exception e) {
            log.error("{}", value, e);
        }
        return json;
    }

    /**
     * 获取泛型的Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  元素类
     * @return JavaType Java类型
     * @since 1.0
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    /**
     * @param jsonStr
     * @param type
     * @return json数据转换成list
     */
    public static <T extends Collection> T fromJsonToCollection(String jsonStr, JavaType type) {
        if (jsonStr == null || jsonStr.length() == 0) {
            return null;
        }
        try {
            return (T) mapper.readValue(jsonStr, type);
        } catch (IOException e) {
            return null;
        }
    }


    /**
     * 两个泛型参数
     * objClass 类型为
     * public class Clazz<S,M>{
     * public S data;
     * public M extend;
     * }
     *
     * @param json                带解析字符串
     * @param objClass            本类
     * @param objClassInnerClass1 本类中韩泛型字段
     * @param objClassInnerClass2 本类中韩泛型字段
     * @param <S>
     * @return
     */
    public static <T, S, M> T getObj(String json, Class<T> objClass, Class<S> objClassInnerClass1, Class<M> objClassInnerClass2) {
        try {
            //这里直接将泛型转换为Class，
//            final SimpleType javaType2 = (SimpleType) mapper.constructType(objClassInnerClass);
            // 构建JsonObj类型的转化对象
//            final SimpleType javaType = (SimpleType) mapper.constructType(objClass);
            Object readValue = mapper.readValue(json, new TypeReference<T>() {
                @Override
                public Type getType() {
                    return TypeUtils.parameterize(objClass, objClassInnerClass1, objClassInnerClass2);
                }
            });
            return (T) readValue;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 一个泛型参数
     *
     * @param json
     * @param objClass
     * @param objClassInnerClass
     * @param <T>
     * @param <C>
     * @return
     */
    public static <T, C> T getObj2(String json, Class<T> objClass, Class<C> objClassInnerClass) {
        try {
            //这里直接将泛型转换为Class，
//            final SimpleType javaType2 = (SimpleType) mapper.constructType(objClassInnerClass);
            // 构建JsonObj类型的转化对象
//            final SimpleType javaType = (SimpleType) mapper.constructType(objClass);
            Object readValue = mapper.readValue(json, new TypeReference<T>() {
                @Override
                public Type getType() {
                    return TypeUtils.parameterize(objClass, objClassInnerClass);
                }
            });
            return (T) readValue;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 没有泛型参数
     *
     * @param json
     * @param objClass
     * @param <T>
     * @return
     */
    public static <T> T getObj3(String json, Class<T> objClass) {
        try {
            //这里直接将泛型转换为Class，
//            final SimpleType javaType2 = (SimpleType) mapper.constructType(objClassInnerClass);
            // 构建JsonObj类型的转化对象
            final SimpleType javaType = (SimpleType) mapper.constructType(objClass);
            Object readValue = mapper.readValue(json, javaType);
            return (T) readValue;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * @param jsonStr
     * @param clazz
     * @return json数据转换成list
     */
    public static List fromJsonToCollection(String jsonStr, String... clazz) {
        try {
            TypeFactory tf = TypeFactory.defaultInstance();
            Class[] classes = new Class[clazz.length];
            for (int i = 0; i < clazz.length; i++) {
                classes[i] = Class.forName(clazz[i]);
            }
            JavaType javaType = tf.constructParametricType(List.class, classes);
            return mapper.readValue(jsonStr, javaType);
        } catch (Exception e) {
            log.error("", e);
            return new ArrayList<>();
        }
    }
    public static <T> T jsonToBean(String json, Class<T> valueType) {
        try {
            if (json == null || json.trim().isEmpty()) {
                return null;
            }
            if(valueType.getSimpleName().equals(String.class.getSimpleName())){
                return (T) json;
            }
            return mapper.readValue(json, valueType);
        } catch (Exception e) {
            log.error("", e);
            return null;
        }
    }
    /**
     * 获取两个json字符串的差异(数组,list,set不支持)
     * @param json1 json字符串1
     * @param json2 json字符串2
     * @return diff
     */
    public static String getJsonDiff(String json1, String json2) throws Exception {
        JsonNode source = mapper.readTree(json1);
        JsonNode target = mapper.readTree(json2);

        EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION, ADD_ORIGINAL_VALUE_ON_REPLACE);
        JsonNode diff = JsonDiff.asJson(source, target, flags);

        return mapper.writeValueAsString(diff);
    }
    /**
     * 获取两个json字符串的差异(数组,list,set不支持)
     * @param obj1 obj1
     * @param obj2 obj2
     * @return diff
     */
    public static String getObjectDiff(Object obj1, Object obj2) throws Exception {
        JsonNode source = mapper.valueToTree(obj1);
        JsonNode target = mapper.valueToTree(obj2);

//        EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION, ADD_ORIGINAL_VALUE_ON_REPLACE);
//        EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION, OMIT_VALUE_ON_REMOVE);//[{"op":"replace","path":"/itemName","value":"it2"}]
//        EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION, OMIT_MOVE_OPERATION);//[{"op":"replace","path":"/itemName","value":"it2"}]
        EnumSet<DiffFlags> flags = EnumSet.of(OMIT_COPY_OPERATION, ADD_ORIGINAL_VALUE_ON_REPLACE);//[{"op":"replace","path":"/itemName","value":"it2"}]
        JsonNode diff = JsonDiff.asJson(source, target, flags);

        return mapper.writeValueAsString(diff);
    }
    public static void main(String[] args) {
        Student student = new Student().withAge(new Random().nextInt(100)).withAddress("xx");
        Person<Student, Teacher> person = new Person().withId(SnowflakeIdUtils.nextId()).withName("ctl").withData(student);
        System.out.println(objToJsonString(person));
        String jsonArray = "[{\"id\":\"0\",\"name\":\"c0\"},{\"id\":\"1\",\"name\":\"c1\"}]";
        List<Person<Student, Teacher>> personList = fromJsonToCollection(jsonArray, getCollectionType(ArrayList.class, Person.class));
        System.out.println(personList);
        jsonArray = "[{\"id\":\"1666357773388091392\",\"name\":\"ctl\",\"data\":{\"age\":58,\"address\":\"xx\"},\"extend\":{\"slary\":10000,\"name\":\"t1\"}},{\"id\":\"1666357833538605056\",\"name\":\"ctl\",\"data\":{\"age\":63,\"address\":\"zz\"},\"extend\":{\"slary\":20000,\"name\":\"t2\"}}]";
        personList = fromJsonToCollection(jsonArray, getCollectionType(ArrayList.class, Person.class));
        System.out.println(personList);
        //student=map
        personList = fromJsonToCollection(jsonArray, getCollectionType(ArrayList.class, getCollectionType(Person.class, Student.class, Teacher.class).getRawClass()));
        System.out.println(personList);
        try {
            //student=student
            personList = mapper.readValue(jsonArray, new TypeReference<List<Person<Student, Teacher>>>() {
            });
        } catch (Exception e) {
            log.error("", e);
        }
        System.out.println(personList);
        String personJson = "{\"id\":\"1666357773388091392\",\"name\":\"ctl\",\"data\":{\"age\":58,\"address\":\"xx\"},\"extend\":{\"slary\":10000,\"name\":\"t1\"}}";
        try {
            //student=student
            Person<Student, Teacher> personStu = mapper.readValue(personJson, new TypeReference<Person<Student, Teacher>>() {
            });
            System.out.println(personStu);
            //student=map
            personStu = getObj(personJson, Person.class, Student.class, Teacher.class);
            Person1 obj2 = getObj2(personJson, Person1.class, Student.class);
            personStu = getObj3(personJson, Person.class);
            System.out.println("show:" + personStu);


            String arg = "{\"bindGoodsList\": [{\"sku\": \"1003\", \"ean\": [], \"itemName\": \"榛子\", \"position\": 0, \"priceChangeTime\": 1698734376373}], \"time\": 1698734376373, \"bindType\": \"WEB\", \"templateName\": \"多页轮播模版\", \"creator\": \"62f5ea9eada83e4c3740f195\", \"_class\": \"com.hanshow.wise.next.module.device.model.pojo.BindHistory\"}\u0002{\"bindGoodsList\": [{\"sku\": \"1003\", \"ean\": [], \"itemName\": \"榛子\", \"position\": 1, \"priceChangeTime\": 1698820535327}, {\"sku\": \"1003\", \"ean\": [], \"itemName\": \"榛子\", \"position\": 2, \"priceChangeTime\": 1698820535327}], \"time\": 1698820535327, \"bindType\": \"WEB\", \"templateName\": \"多页轮播模版\", \"creator\": \"62f5ea9eada83e4c3740f195\", \"_class\": \"com.hanshow.wise.next.module.device.model.pojo.BindHistory\"}\u0002{\"bindGoodsList\": [{\"sku\": \"1003\", \"ean\": [], \"itemName\": \"榛子\", \"position\": 0, \"priceChangeTime\": 1698820539008}], \"time\": 1698820539008, \"bindType\": \"WEB\", \"templateName\": \"多页轮播模版\", \"creator\": \"62f5ea9eada83e4c3740f195\", \"_class\": \"com.hanshow.wise.next.module.device.model.pojo.BindHistory\"}\u0002{\"bindGoodsList\": [{\"sku\": \"1001\", \"ean\": [], \"itemName\": \"大荔西瓜\", \"position\": 0, \"priceChangeTime\": 1699944863571}], \"time\": 1699944863571, \"bindType\": \"WEB\", \"templateName\": \"轮播+全屏营销\", \"creator\": \"62f5ea9eada83e4c3740f195\", \"_class\": \"com.hanshow.wise.next.module.device.model.pojo.BindHistory\"}";
            arg = "[" + arg.replaceAll("\\u0002", ",") + "]";
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            JsonNode jsonNode = mapper.readTree(arg);
            System.out.println(jsonNode.toString());
            List list = mapper.readValue(arg, new TypeReference<List<BindGoods>>() {
            });

            System.out.println(mapper.writeValueAsString(list));

            TypeFactory tf = TypeFactory.defaultInstance();
            JavaType javaType = tf.constructParametricType(List.class, Class.forName("com.ctl.common.model.BindGoods"));
            list = mapper.readValue(arg, javaType);

            System.out.println(mapper.writeValueAsString(list));
            List list1 = fromJsonToCollection(arg, "com.ctl.common.model.BindGoods");
            System.out.println(list1);
            Goods goods1 =new Goods().withSku("1").withItemName("it1").withEan(Arrays.asList("1","2"));
            Goods goods2 =new Goods().withSku("1").withItemName("it2").withEan(Arrays.asList("2","3","1"));
            System.out.println(getObjectDiff(goods1,goods2));
        } catch (Exception e) {
            log.error("", e);
        }
    }

}
